#include <stdlib.h>
#include <string.h>
#include "qe_vars.h"

/* free content */
static void q_freesimplevariable(union q_simplevariable* variable, enum q_vartype type)
{
	int i;
	switch(type)
	{
		case VAR_ARRAY:
			if(variable->array.length) /* paranoid */
			{
				for(i = 0; i < variable->array.length; ++i)
					q_freesimplevariable(&variable->array.elems[i], variable->array.type);
				free(variable->array.elems);
			}
			break;
		case VAR_STRING:
			if(variable->string.length)
				free(variable->string.chars);
			break;
		case VAR_STRUCT:
			/* TO DO: improve tha shit */
			if(variable->structure.length)
			{
				for(i = 0; i < variable->structure.length; ++i)
					q_freevariable(&variable->structure.vars[i]);
				free(variable->structure.vars);
			}
			break;
		default:
			break;
	}
}

/* deletes variable content (so you can delete variableiable ptr without memory leak) */
void q_freevariable(struct q_variable* variable)
{
	if(!(--variable->refcount))
		q_freesimplevariable(&variable->data, variable->type);
}

/* deletes variable content unconditionally */
void q_removevariable(struct q_variable* variable)
{
	q_freesimplevariable(&variable->data, variable->type);
}

struct q_variable* q_createbool(int value, int constant)
{
	struct q_variable* tmp;
	if(!(tmp = (struct q_variable*)malloc(sizeof(struct q_variable))))
		return NULL;
	tmp->type = VAR_BOOL;
	tmp->constant = (constant)?(1):(0);
	tmp->refcount = 1;
	tmp->data.b = (value)?(1):(0);
	return tmp;
}


struct q_variable* q_createint(int value, int constant)
{
	struct q_variable* tmp;
	if(!(tmp = (struct q_variable*)malloc(sizeof(struct q_variable))))
		return NULL;
	tmp->type = VAR_INT;
	tmp->constant = (constant)?(1):(0);
	tmp->refcount = 1;
	tmp->data.i = value;
	return tmp;
}

struct q_variable* q_createfloat(double value, int constant)
{
	struct q_variable* tmp;
	if(!(tmp = (struct q_variable*)malloc(sizeof(struct q_variable))))
		return NULL;
	tmp->type = VAR_FLOAT;
	tmp->constant = (constant)?(1):(0);
	tmp->refcount = 1;
	tmp->data.f = value;
	return tmp;
}

struct q_variable* q_createstring(const char* const value, int constant)
{
	int length;
	struct q_variable* tmp;
	if(!value)
		return NULL;
	if(!(tmp = (struct q_variable*)malloc(sizeof(struct q_variable))))
		return NULL;
	tmp->type = VAR_STRING;
	tmp->constant = (constant)?(1):(0);
	tmp->refcount = 1;
	if((length = strlen(value)))
	{
		if(!(tmp->data.string.chars = (char*)malloc(length)))
		{
			free(tmp);
			return NULL;
		}
		memcpy(tmp->data.string.chars, value, length);
	}
	else
	{
		tmp->data.string.length = 0;
		tmp->data.string.chars = NULL;
	}
	return tmp;
}

struct q_variable* q_createarray(int length, int constant)
{
	return NULL;
}

struct q_variable* q_createstructure(int length, int constant)
{
	return NULL;
}

/* TO DO: finish struct copying */
static int q_copysimplevariable(union q_simplevariable* variable, enum q_vartype type, union q_simplevariable* result)
{
	int i, j;
	int length;

	switch(type)
	{
		case VAR_BOOL:
			result->b = variable->b;
			return 0;
		case VAR_INT:
			result->i = variable->i;
			return 0;
		case VAR_FLOAT:
			result->f = variable->f;
			return 0;
		case VAR_STRING:
			length = variable->string.length;
			result->string.length = length;
			if(length)
			{
				if(!(result->string.chars = (char*)malloc(length)))
					return -1;
				memcpy(result->string.chars, variable->string.chars, length);
			}
			return 0;
		case VAR_ARRAY:
			length = variable->array.length;
			result->array.length = length;
			result->array.type = variable->array.type;
			if(length)
			{
				if(!(result->array.elems = (union q_simplevariable*)malloc(length * sizeof(union q_simplevariable))))
					return -1;
				for(i = 0; i < length; ++i)
					if(q_copysimplevariable(&variable->array.elems[i], variable->array.type, &result->array.elems[i]) == -1)
					{
						// shit happens //
						for(j = 0; j < i; ++j)
							q_freesimplevariable(&result->array.elems[i], result->array.type);
						free(result->array.elems);
						return -1;
					}
			}
			return 0;
		case VAR_STRUCT:
			return -1;
		default:
			return -1;
	}
}

/* copy variable */
/* RETURN: ptr2variable or NULL on error */
struct q_variable* q_copyvariable(struct q_variable* variable, int constant)
{
	struct q_variable* tmp;
	if(!variable || !(tmp = (struct q_variable*)malloc(sizeof(struct q_variable))))
		return NULL;
	tmp->type = variable->type;
	tmp->refcount = 1;
	switch(constant)
	{
		case 0:
		case 1:
			tmp->constant = constant;
			break;
		default:
			tmp->constant = variable->constant;
	}
	if(q_copysimplevariable(&variable->data, variable->type, &tmp->data) == -1)
	{
		free(tmp);
		return NULL;
	}
	else 
		return tmp;
}

/* gets reference to variable */
struct q_variable* q_getref(struct q_variable* variable)
{
	if(!variable)
		return NULL;
	++variable->refcount;
	return variable;
}
